/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.prefs;

import com.google.common.collect.Maps;

import net.sourceforge.perforce.ui.IPerforceUIConstants;
import net.sourceforge.perforce.ui.P4DecoratorConfiguration;
import net.sourceforge.perforce.ui.PerforceDecorator;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.internal.Policy;

import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.StringFieldEditor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;

import java.util.Map;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class P4DecoratorPreferencesPage extends FieldEditorPreferencePage implements
    IWorkbenchPreferencePage {
  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the Project / Example Text pair. */
  private TextPair projectTextPair;
  /** Stores the Folder / Example Text pair. */
  private TextPair folderTextPair;
  /** Stores the File / Example Text pair. */
  private TextPair fileTextPair;
  /** Text control of dirty flag editor. */
  private Text dirty;

  // -------------------------------------------------------------------------
  // Public Constructors

  /** Constructor for P4DecoratorPreferencesPage. */
  public P4DecoratorPreferencesPage() {
    super(FieldEditorPreferencePage.GRID);
    setPreferenceStore(PerforceUIPlugin.getPlugin().getPreferenceStore());
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  public void init(IWorkbench workbench) {
  }

  @Override
  public boolean performOk() {
    boolean result = super.performOk();
    PerforceDecorator.refresh();

    return result;
  }

  // ---------------------------------------------------------------------------
  // Protected Methods

  @Override
  protected void createFieldEditors() {
    createFieldEditors(getFieldEditorParent());
  }

  protected void createFieldEditors(Composite parent) {
    Group textGroup = new Group(parent, SWT.NULL);
    GridLayout layout = new GridLayout();
    layout.numColumns = 3;
    textGroup.setLayout(layout);
    GridData data = new GridData(GridData.FILL_HORIZONTAL);
    data.horizontalSpan = 2;
    textGroup.setLayoutData(data);
    textGroup.setText(Policy.bind("P4DecoratorPreferencesPage.TextGroup")); //$NON-NLS-1$
    textGroup.setFont(JFaceResources.getDialogFont());

    createLabel(textGroup, Policy.bind("P4DecoratorPreferencesPage.TestGroupLabel"), 3); //$NON-NLS-1$

    projectTextPair =
        createFormatEditorControl(textGroup, IPerforceUIConstants.PREF_PROJECTTEXTFORMAT, Policy
            .bind("P4DecoratorPreferencesPage.ProjectFormatLabel"), //$NON-NLS-1$
            Policy.bind("P4DecoratorPreferencesPage.ProjectFormatButton"), //$NON-NLS-1$
            getFolderBindingDescriptions());
    folderTextPair =
        createFormatEditorControl(textGroup, IPerforceUIConstants.PREF_FOLDERTEXTFORMAT, Policy
            .bind("P4DecoratorPreferencesPage.FolderFormatLabel"), //$NON-NLS-1$
            Policy.bind("P4DecoratorPreferencesPage.FolderFormatButton"), //$NON-NLS-1$
            getFolderBindingDescriptions());
    fileTextPair =
        createFormatEditorControl(textGroup, IPerforceUIConstants.PREF_FILETEXTFORMAT, Policy
            .bind("P4DecoratorPreferencesPage.FileFormatLabel"), //$NON-NLS-1$
            Policy.bind("P4DecoratorPreferencesPage.FileFormatButton"), //$NON-NLS-1$
            getFileBindingDescriptions());

    StringFieldEditor outgoing =
        new InternalStringFieldEditor(IPerforceUIConstants.PREF_DIRTY_FLAG, Policy
            .bind("P4DecoratorPreferencesPage.OutgoingLabel"), //$NON-NLS-1$
            textGroup);
    outgoing.getTextControl(textGroup).addModifyListener(new ModifyListener() {
      public void modifyText(ModifyEvent e) {
        updateExamples();
      }
    });
    addField(outgoing);
    dirty = outgoing.getTextControl(textGroup);

    StringFieldEditor added =
        new InternalStringFieldEditor(IPerforceUIConstants.PREF_ADDED_FLAG, Policy
            .bind("P4DecoratorPreferencesPage.AddedLabel"), //$NON-NLS-1$
            textGroup);
    added.getTextControl(textGroup).addModifyListener(new ModifyListener() {
      public void modifyText(ModifyEvent e) {
        updateExamples();
      }
    });
    addField(added);



    Group iconsGroup = new Group(parent, SWT.NULL);
    layout = new GridLayout();
    layout.numColumns = 3;
    iconsGroup.setLayout(layout);
    data = new GridData(GridData.FILL_HORIZONTAL);
    data.horizontalSpan = 2;
    iconsGroup.setLayoutData(data);
    iconsGroup.setText(Policy.bind("P4DecoratorPreferencesPage.IconsGroup")); //$NON-NLS-1$
    iconsGroup.setFont(JFaceResources.getDialogFont());

    BooleanFieldEditor indicateOpenedForEdit =
        new BooleanFieldEditor(IPerforceUIConstants.PREF_INDICATE_OPENED_FOR_EDIT, Policy
            .bind("P4DecoratorPreferencesPage.IndicateOpenedForEdit"), //$NON-NLS-1$
            iconsGroup);
    addField(indicateOpenedForEdit);

    BooleanFieldEditor indicateHasRemote =
        new BooleanFieldEditor(IPerforceUIConstants.PREF_INDICATE_HAS_REMOTE, Policy
            .bind("P4DecoratorPreferencesPage.IndicateHasRemote"), //$NON-NLS-1$
            iconsGroup);
    addField(indicateHasRemote);

    BooleanFieldEditor indicateOpenedForAdd =
        new BooleanFieldEditor(IPerforceUIConstants.PREF_INDICATE_OPENED_FOR_ADD, Policy
            .bind("P4DecoratorPreferencesPage.IndicateOpenedForAdd"), //$NON-NLS-1$
            iconsGroup);
    addField(indicateOpenedForAdd);

    BooleanFieldEditor indicateNewResource =
        new BooleanFieldEditor(IPerforceUIConstants.PREF_INDICATE_NEW_RESOURCE, Policy
            .bind("P4DecoratorPreferencesPage.IndicateNewResource"), //$NON-NLS-1$
            iconsGroup);
    addField(indicateNewResource);

    BooleanFieldEditor indicateOpenedByOther =
        new BooleanFieldEditor(IPerforceUIConstants.PREF_INDICATE_OPENED_BY_OTHER, Policy
            .bind("P4DecoratorPreferencesPage.IndicateOpenedByOther"), //$NON-NLS-1$
            iconsGroup);
    addField(indicateOpenedByOther);

    // TODO: Change message key
    BooleanFieldEditor showDirty =
        new BooleanFieldEditor(PerforceUIPlugin.CALCULATE_DIRTY_PREFERENCE, Policy
            .bind("PerforcePreferencePage.calculateDirty"), //$NON-NLS-1$
            parent);
    addField(showDirty);
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  /**
   * Called to create a new format editor for the given preference.
   * 
   * @param parent the parent composite.
   * @param prefName the name of the preference.
   * @param title the label text
   * @param buttonText text that appears on the button
   * @param bindings the bindings
   * @return a TextPair instance.
   */
  private TextPair createFormatEditorControl(Composite parent, String prefName, String title,
      String buttonText, Map<String, String> bindings) {
    FormatFieldEditor fieldEditor =
        new FormatFieldEditor(prefName, title, buttonText, bindings, parent);
    fieldEditor.getTextControl(parent).setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    fieldEditor.getTextControl(parent).addModifyListener(new ModifyListener() {
      public void modifyText(ModifyEvent e) {
        updateExamples();
      }
    });
    addField(fieldEditor);

    createLabel(parent, Policy.bind("P4DecoratorPreferencesPage.ExampleLabel"), 1);//$NON-NLS-1$
    Text example = createText(parent, false);
    createLabel(parent, "", 1); //$NON-NLS-1$

    return new TextPair(fieldEditor.getTextControl(parent), example);
  }

  /**
   * Crates a Text control.
   * 
   * @param parent the control's parent.
   * @param editable indicates whether the control is editable or not.
   * @return the new Text control.
   */
  private Text createText(Composite parent, boolean editable) {
    Text result = new Text(parent, SWT.BORDER);
    result.setEditable(editable);
    result.setFont(parent.getFont());

    result.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    return result;
  }

  /**
   * Creates a new Label control.
   * 
   * @param parent the Label's parent control.
   * @param text the text to display.
   * @param span number of columns to span.
   * @return the new Label control.
   */
  private Label createLabel(Composite parent, String text, int span) {
    Label result = new Label(parent, SWT.LEFT);
    result.setText(text);
    result.setFont(parent.getFont());

    GridData data = new GridData();
    data.horizontalSpan = span;
    data.horizontalAlignment = GridData.FILL;
    result.setLayoutData(data);

    return result;
  }

  /** Called to update the controls displaying the examples. */
  private void updateExamples() {
    Map<String, String> bindings = Maps.newHashMap();
    bindings.put(P4DecoratorConfiguration.P4PORT, "perforce:1666"); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.P4CLIENT, "acme-client"); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.P4USER, "username"); //$NON-NLS-1$

    bindings.put(P4DecoratorConfiguration.HAVE_REV, "#3"); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.HEAD_REV, "#4"); //$NON-NLS-1$

    bindings.put(P4DecoratorConfiguration.DIRTY_FLAG, dirty.getText());

    bindings.put(P4DecoratorConfiguration.RESOURCE_NAME, "project"); //$NON-NLS-1$
    setProjectFormatExample(bindings);
    bindings.put(P4DecoratorConfiguration.RESOURCE_NAME, "folder"); //$NON-NLS-1$
    setFolderFormatExample(bindings);
    bindings.put(P4DecoratorConfiguration.RESOURCE_NAME, "file.txt"); //$NON-NLS-1$
    setFileFormatExample(bindings);
  }

  /**
   * Called to set the example for file format.
   * 
   * @param bindings contains the example bindings.
   */
  private void setFileFormatExample(Map<String, String> bindings) {
    String example =
        P4DecoratorConfiguration.bind(fileTextPair.getFormatControl().getText(), bindings);
    fileTextPair.getExampleControl().setText(example);
  }

  /**
   * Called to set the example for folder format.
   * 
   * @param bindings contains the example bindings.
   */
  private void setFolderFormatExample(Map<String, String> bindings) {
    String example =
        P4DecoratorConfiguration.bind(folderTextPair.getFormatControl().getText(), bindings);
    folderTextPair.getExampleControl().setText(example);
  }

  /**
   * Called to set the example for project format.
   * 
   * @param bindings contains the example bindings.
   */
  private void setProjectFormatExample(Map<String, String> bindings) {
    String example =
        P4DecoratorConfiguration.bind(projectTextPair.getFormatControl().getText(), bindings);
    projectTextPair.getExampleControl().setText(example);
  }

  /**
   * Returns descriptions for the valid file bindings.
   * 
   * @return map with possible file bindings.
   */
  private Map<String, String> getFileBindingDescriptions() {
    Map<String, String> bindings = Maps.newHashMap();
    bindings.put(P4DecoratorConfiguration.RESOURCE_NAME, Policy
        .bind("P4DecoratorPreferencesPage.BindingResourceName")); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.HAVE_REV, Policy
        .bind("P4DecoratorPreferencesPage.BindingHaveRevision")); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.HEAD_REV, Policy
        .bind("P4DecoratorPreferencesPage.BindingHeadRevision")); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.DIRTY_FLAG, Policy
        .bind("P4DecoratorPreferencesPage.BindingFileDirtyFlag")); //$NON-NLS-1$
    return bindings;
  }

  /**
   * Returns the descriptions of possible folder bindings.
   * 
   * @return map containing the possible folder bindings.
   */
  private Map<String, String> getFolderBindingDescriptions() {
    Map<String, String> bindings = Maps.newHashMap();
    bindings.put(P4DecoratorConfiguration.RESOURCE_NAME, Policy
        .bind("P4DecoratorPreferencesPage.BindingResourceName")); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.P4PORT, Policy
        .bind("P4DecoratorPreferencesPage.BindingP4port")); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.P4CLIENT, Policy
        .bind("P4DecoratorPreferencesPage.BindingP4client")); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.P4USER, Policy
        .bind("P4DecoratorPreferencesPage.BindingP4user")); //$NON-NLS-1$
    bindings.put(P4DecoratorConfiguration.DIRTY_FLAG, Policy
        .bind("P4DecoratorPreferencesPage.BindingFolderDirtyFlag")); //$NON-NLS-1$
    return bindings;
  }

  // ----------------------------------------------------------------------
  // Private Nested Classes

  /** This class provides a "fixed" version of the StringFieldEditor. */
  private static class InternalStringFieldEditor extends StringFieldEditor {
    // ---------------------------------------------------------------------
    // Public Constructors

    /** Constructor for InternalStringFieldEditor. */
    public InternalStringFieldEditor(String name, String labelText, Composite parent) {
      super(name, labelText, parent);
    }

    // -----------------------------------------------------------------------
    // Protected Methods

    @Override
    protected void createControl(Composite parent) {
      GridLayout layout = (GridLayout) parent.getLayout();
      doFillIntoGrid(parent, layout.numColumns);
    }
  }

  /**
   * This class is used to associate the format text fields with their example
   * controls.
   */
  private static class TextPair {
    // -----------------------------------------------------------------------
    // Private Variables

    /** Stores a reference to the format control. */
    private Text formatControl;
    /** Stores a reference to the example control. */
    private Text exampleControl;

    // ---------------------------------------------------------------------
    // Public Constructors

    /** Constructor for TextPair. */
    public TextPair(Text newFormatControl, Text newExampleControl) {
      formatControl = newFormatControl;
      exampleControl = newExampleControl;
    }

    //--------------------------------------------------------------------------
    // Public Methods

    /**
     * Returns the format control.
     * 
     * @return the format text control.
     */
    public Text getFormatControl() {
      return formatControl;
    }

    /**
     * Returns the exmaple control.
     * 
     * @return the example text control.
     */
    public Text getExampleControl() {
      return exampleControl;
    }
  }
}
